package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

/**
 * 全局过滤器，校验是否登录
 *          自定义过滤器 GlobalLoginFilter
 *              不算自定义过滤器默认执行9大过滤器
 *              一共10大过滤器
 */

@Component
public class GlobalLoginFilter implements GlobalFilter, Ordered {

    //匹配URI路径
    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    //浏览器中的token令牌
    public static final String TOKEN = "token";
    //浏览器中临时用户的名称
    public static final String USERTEMPID = "userTempId";

    //原生的redisTemplate存在乱码问题，需要进行单独配置指定Redis编码
    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${auth.url}")
    private String[] authUrl;

    public static final String LOGINURL= "http://passport.gmall.com/login.html?originUrl=";

    //全局过滤器的执行方法
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //1.获取当前URI
        String path = request.getURI().getPath();

        //2.内部资源不允许浏览器访问 /**/inner/**  登录与未登录都不允许访问
        if (antPathMatcher.match("/**/inner/**",path)){
            //内部资源，无权访问
            return getVoidMono(response,ResultCodeEnum.PERMISSION);
        }

        //3.判断当前URI是否必须登录  以/auth开始的就要登录了 其他的不需要登录
        String userId = getUserId(request);
        // 1）弹出小窗口   异步  不刷新页面 Ajax
        //异步 /**/auth/**  网关直接连接redis校验用户是否登录 未登录直接返回201(失败)  登录直接放行
        if (antPathMatcher.match("/**/auth/**",path)){
            //当前path路径是需要登录的 判断用户是否登录
            if (StringUtils.isEmpty(userId)){
                //为空，未登录
                return getVoidMono(response,ResultCodeEnum.LOGIN_AUTH);
            }
        }
        // 2）转发登录页面   同步  刷新页面  windos.location.href
        //同步 /trade.html  /order.html... 网关路由到后面的微服务
        for (String url : authUrl) {
            if (path.indexOf(url) != -1 && StringUtils.isEmpty(userId)){
                //说明该服务需要登录 在判断是否登录 未登录
                //如果未登录就重定向到登录页面   HttpStatus.SEE_OTHER重定向的状态码：303
                response.setStatusCode(HttpStatus.SEE_OTHER);
                //url地址
                try {
                    response.getHeaders().set(HttpHeaders.LOCATION,
                            LOGINURL + URLEncoder.encode(request.getURI().toString(),"utf-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return response.setComplete();//表示完成之后可以响应了
            }
        }
        //加入购物车 ：
        //当前是网关 传递用户id给后面的微服务  请求头Key：userId  value：“3”
        //传递真实用户id
        if (!StringUtils.isEmpty(userId)){//判断userId是否为空，因为首页是不需要穿用户id的
            request.mutate().header("userId", userId);
        }
        //传递临时用户id
        String userTempId = getUserTempId(request);
        if (!StringUtils.isEmpty(userTempId)){
            request.mutate().header("userTempId", userTempId);
        }

        //放行
        return chain.filter(exchange);
    }

    private Mono<Void> getVoidMono(ServerHttpResponse response,ResultCodeEnum resultCodeEnum) {
        Result<Object> r = Result.build(null, resultCodeEnum);
        String result = JSONObject.toJSONString(r);
        DataBuffer dataBuffer = response.bufferFactory().wrap(result.getBytes());

        //解决Response乱码问题  Content-Type:application/json;charset=utf-8
        response.getHeaders().set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
        return response.writeWith(Mono.just(dataBuffer));
    }

    //获取临时用户id
    private String getUserTempId(ServerHttpRequest request) {
        //请求头 异步
        String userTempId = request.getHeaders().getFirst(USERTEMPID);
        if (StringUtils.isEmpty(userTempId)){//同步
            //如果为空在从cookie中拿
            HttpCookie httpCookie = request.getCookies().getFirst(USERTEMPID);
            if (null != httpCookie){
                userTempId = httpCookie.getValue();
            }
        }
        return userTempId;
    }

    //判断用户是否登录  获取用户id
    private String getUserId(ServerHttpRequest request) {
        //异步请求：令牌放在了请求头中；同步请求：令牌放在了cookie中
        //获取令牌 先从请求头之中拿
        String token = request.getHeaders().getFirst(TOKEN);
        if (StringUtils.isEmpty(token)){
            //如果为空在从cookie中拿
            HttpCookie httpCookie = request.getCookies().getFirst(TOKEN);
            if (null != httpCookie){
                token = httpCookie.getValue();
            }
        }
        //1.有 去缓存查询用户id是否存在
        if (!StringUtils.isEmpty(token)){
            //是否存在令牌
            if (redisTemplate.hasKey(token)){
                return (String) redisTemplate.opsForValue().get(token);
            }
        }
        //2.没有 未登录
        return null;
    }

    //过滤器执行顺序  负整数最大值  0   正整数最大值   越小越先执行
    @Override
    public int getOrder() {
        return 0;
    }
}
